import csv, codecs
import exceptions
import sys
import getopt

VERSION = '0.0.1'
REVISION = "$Revision: 39 $".replace("$","") #SVN Attribute that gets updated on each check-in (we remove the $ $'s to make it readable


#Nifty little class to make dict()s accessible by javascript dot notation
#From here: http://code.activestate.com/recipes/52308-the-simple-but-handy-collector-of-a-bunch-of-named/
class Bunch(dict):
    def __init__(self,**kw):
        dict.__init__(self,kw)
        self.__dict__ = self

#Supported code types
codeTypes = Bunch(
    Referential = 0,
    ReverseReferential = 1,
    SemiStructured = 2,
    AlignedSemiStructured = 3
)

#Supported Category descriptions
categoryTypes = Bunch(
    PrefixEmbedded = 0,
    PreDefinedColumn = 1,
    TitledColumn = 2,
    Monolingual = 3
)

outputTypes = Bunch(
    DDI31   = 0
)

# SEE - http://docs.python.org/library/csv.html#csv-examples 
def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    for line in unicode_csv_data:
        yield line.encode('utf-8')

class converter():
    def __init__(self,file,header=True,sep=",",defaultLanguage='en',
                 codetype=codeTypes.Referential,
                 categorytype=categoryTypes.PrefixEmbedded,
                 output=outputTypes.DDI31):

        if codetype not in codeTypes.values():
            raise InvalidCodeTypeError(codetype)        
        if categorytype not in categoryTypes.values():
            raise InvalidCategoryTypeError(categorytype)
        if output not in outputTypes.values():
            raise InvalidOutputTypeError(output)

        self.header       = bool(header)
        self.filename     = file
        self.codetype     = codetype 
        self.categorytype = categorytype 
        self.outputtype   = output
        self.separator    = sep
        self.defaultLang  = defaultLanguage
        with open(self.filename, 'rb') as f: # Uncomment for Python 2.5 or greater
            f = codecs.open(self.filename, 'rb')     # Ditto
            self.csv      = csv.reader(f, delimiter=self.separator)

    # A generator to go through the file and convert stuff
    # yields a dict with the code and categories in it
    # This makes things easier on the UI for Virgil as we can update the progress easier
    # NOTE: THIS YIELDS, IT DOES NOT RETURN!
    # x = { code:   A,
    #       parent: B,
    #       category: { labels       : {'lang1' : 'label', 'lang2' : 'label', ... , 'langN' : 'label'},
    #                   descriptions : {'lang1' : 'description', 'lang2' : 'description', ... , 'langN' : 'description'}
    #                 }
    #     }
    def convert(self):
        #We need to track where we are in the hierarchy for the semistructured stuff. This only changes for those code types
        parents = [None]
        first = True
        for entries in self.csv:
            if self.header and first:
                first = False
                continue
            o = Bunch( code   = None,
                       parent = None)
            #Do Codes first, as these dictate where the categories start in the CSV
            if   self.codetype == codeTypes.Referential:
                o.code,o.parent = entries[:2]
                entries     = entries[2:]
            elif self.codetype == codeTypes.ReverseReferential:
                o.parent,o.code = entries[:2]
                entries     = entries[2:]
            elif self.codetype == codeTypes.AlignedSemiStructured or self.codetype == codeTypes.SemiStructured:
                count = 1
                while o.code == None:
                    x = entries.pop(0)
                    if x != '':
                        o.code = x
                        # Always ad ourself as we never know if we are a parent
                        if count < len(parents):
                            parents[count] = x 
                        else:
                            # This will throw an exception if the user intents by two instead of one - this isn't a bug
                            parents.append(x)
                        o.parent = parents[count-1]
                    else:
                        count = count + 1
                while entries[0] == '':
                    entries.pop(0)

            o.labels=dict()
            o.descriptions=dict()
            #Do Categories
            if   self.categorytype == categoryTypes.PrefixEmbedded:
                pairs = zip(entries[0::2],entries[1::2])
                for label,desc in pairs:
                    lang,label = label.split(';',1)
                    lang  = lang.strip()
                    label = label.strip()
                    o.labels[lang] = label

                    lang,desc  = desc.split(';',1)
                    lang  = lang.strip()
                    desc  = desc.strip()
                    o.descriptions[lang] = desc

            elif self.categorytype == categoryTypes.PreDefinedColumn:
                triples = zip(entries[0::3],entries[1::3],entries[2::3])
                for lang,label,desc in triples:
                    o.labels[lang]       = unicode(label,"utf-8")
                    o.descriptions[lang] = unicode(desc,"utf-8")

            elif self.categorytype == categoryTypes.TitledColumn:
                pass
            elif self.categorytype == categoryTypes.Monolingual:
                o.labels[self.defaultLang] = entries[0]
                o.descriptions[self.defaultLang] = entries[1]
            yield o
    

class InvalidOutputTypeError(exceptions.Exception):
    def __init__(self,output):
        self.output = output
        return

    def __str__(self):
        print "","Invalid output - %s" % self.output

class InvalidCodeTypeError(exceptions.Exception):
    def __init__(self,code):
        self.code = code
        return

    def __str__(self):
        print "","Invalid code - %s" % self.code

class InvalidCategoryTypeError(exceptions.Exception):
    def __init__(self,category):
        self.category = category
        return

    def __str__(self):
        print "","Invalid category - %s" % self.category


